<?php

declare(strict_types=1);

namespace App\Controller;

use App\Model\User;
use App\Tasks\BroadcastTask;
use Hyperf\Contract\OnCloseInterface;
use Hyperf\Contract\OnMessageInterface;
use Hyperf\Contract\OnOpenInterface;
use Hyperf\Task\Task;
use Hyperf\Task\TaskExecutor;
use Swoole\Http\Request;
use Swoole\Server;
use Swoole\Websocket\Frame;
use Swoole\WebSocket\Server as WebSocketServer;
use Hyperf\Di\Annotation\Inject;
use App\Services\ChatService;


/**
 * Class WebSocketController
 * @package App\Controller
 */
class WebSocketController extends AbstractController implements OnMessageInterface, OnOpenInterface, OnCloseInterface
{
    /**
     * @Inject
     * @var ChatService
     */
    protected $chatService;


    public function onOpen(WebSocketServer $server, Request $request): void
    {
        //用户认证
        $token = $this->request->input('token');
        $user = User::query()->where(['token' => $token])->first();
        if (!$user) {
            $server->push($request->fd, json_encode([
                'message' => '用户认证失败'
            ]));
            $server->close($request->fd);
            return;
        }

        $redis = $this->container->get(\Redis::class);

        $redis->hSet($this->chatService->userIdMapFd, (string)$user->id, (string)$request->fd); //记录用户与连接映射
        $redis->hSet($this->chatService->fdMapUserId, (string)$request->fd, (string)$user->id); //记录连接映射与用户

        $onLineFds = $this->chatService->getConnections();

        //广播其他人我上线了
        $task = $this->container->get(TaskExecutor::class);
        $task->execute(new Task([BroadcastTask::class, 'handle'], [
            ['data' => ['user' => $user->toArray()], 'channel' => 'online'],
            $request->fd,
            $onLineFds
        ]));
    }

    public function onMessage(WebSocketServer $server, Frame $frame): void
    {
        $receiveData = json_decode($frame->data, true);

        if (empty($receiveData['channel']) || empty($receiveData['data'])) {
            return;
        }

        if (method_exists($this->chatService, $receiveData['channel'])) {
            call_user_func(
                [$this->chatService, $receiveData['channel']],
                $server,
                $frame->fd,
                $receiveData['data']
            );
        }
    }

    public function onClose(Server $server, int $fd, int $reactorId): void
    {
        $redis = $this->container->get(\Redis::class);
        //获取当前离开的用户信息
        $userId = $redis->hGet($this->chatService->fdMapUserId, (string)$fd);
        $user = User::query()->where(['id' => (int)$userId])->first();
        $redis->hDel($this->chatService->fdMapUserId, $fd);
        $redis->hDel($this->chatService->userIdMapFd, $userId);

        if ($user) {
            $onlineFds = $this->chatService->getConnections();
            //广播在线用户我已经离开了
            $task = $this->container->get(TaskExecutor::class);
            $task->execute(new Task([BroadcastTask::class, 'handle'], [
                ['data' => [
                    'user' => $user->toArray()
                ], 'channel' => 'offline'],
                $reactorId,
                $onlineFds
            ]));
        }
    }
}
